import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { ChargeCard } from 'src/entities/ChargeCard';
import { ChargeCardLog } from 'src/entities/ChargeCardLog';
import { ChargeCardOrder } from 'src/entities/ChargeCardOrder';
import { ChargeStrategy } from 'src/entities/ChargeStrategy';
import { Merchant } from 'src/entities/Merchant';
import { LocalCacheService } from 'src/local-cache/local-cache.service';
import { Connection, Repository } from 'typeorm';
import { DeviceService } from '../device/device.service';

@Injectable()
export class ChargeCardService {
    private cardKey = 'card_'
    constructor(
        @InjectRepository(ChargeCard) protected repository:Repository<ChargeCard>,
        @InjectRepository(ChargeStrategy) protected strategyRepository:Repository<ChargeStrategy>,
        @InjectRepository(ChargeCardOrder) protected orderRepository:Repository<ChargeCardOrder>,
        @InjectRepository(Merchant) protected merchantRepository:Repository<Merchant>,
        private localCache:LocalCacheService,
        private deviceService:DeviceService,
        private connection: Connection
    ) {}

    private async startTransaction() {
        const queryRunner = this.connection.createQueryRunner();
        await queryRunner.connect();
        await queryRunner.startTransaction()

        return queryRunner
    }

    private cardUpdate(amount, rechargeBalance, rewardBalance, order, card) {
        let cardUpdate:{amount? : string, rewardAmount? : string} = {}
        let cardLog:{[key in keyof ChargeCardLog]? : ChargeCardLog[key]} = {
            userId : order.userId,
            cardId : order.cardId,
            beforeAmount : card.amount,
            beforeRewardAmount : card.rewardAmount
        }

        if(rechargeBalance >= amount) { //充值余额够钱
            order.feeAmount = order.prePayAmount //充电订单中充值余额支出部分
            cardUpdate.amount = String(rechargeBalance - amount) //钱包充值余额，更新钱包
            

            cardLog.amount = String(-amount) //余额部分支付的
            //支出之后钱包的余额记录
            cardLog.afterAmount = cardUpdate.amount
            cardLog.afterRewardAmount = card.rewardAmount //赠送余额不变
        }else {
            let offset = amount - rechargeBalance
            
            order.feeRewardAmount = String(offset) //充电订单中赠送余额支出部分
            cardUpdate.rewardAmount = String(rewardBalance - offset) //钱包赠送余额，更新钱包
            
            cardLog.rewardAmount = String(-offset)  //赠送余额支出部分

            //支出之后钱包的赠送余额记录
            cardLog.afterRewardAmount = cardUpdate.rewardAmount

            if(rechargeBalance > 0) { //如果充值余额有钱的
                order.feeAmount = String(rechargeBalance) //充值余额全部给出
                cardUpdate.amount = '0' //钱包充值余额，更新钱包

                cardLog.amount = String(-rechargeBalance)
                cardLog.afterAmount = cardUpdate.amount
            }

        }

        return {
            cardLog,
            cardUpdate
        }
    }


    card(cardNo:string) {
        return this.repository.findOne({
            where : {cardNo},
            select : ['cardId', 'userId', 'amount', 'rewardAmount', 'status']
        })
    }

    private async defaultStrategy() {
        let strategy:ChargeStrategy
        const strategyCache = await this.localCache.store.get('defaultStrategy')
        if(strategyCache) {
            strategy = JSON.parse(strategyCache)
        }else {
            strategy = await this.strategyRepository.findOne({
                select : ['unitPrice', 'settleUnit', 'rules', 'strategyType', 'cardChargeAmount']
            })
        }

        return strategy
    }

    async strategy(strategyId:number) {
        if(strategyId == 0) return this.defaultStrategy()
        const key = 'strategy:' + strategyId
        let strategy:ChargeStrategy
        const cache = await this.localCache.store.get(key)
        if(cache) {
            strategy = JSON.parse(cache)
        }else {
            const fields:(keyof ChargeStrategy)[] = ['unitPrice', 'settleUnit', 'rules', 'strategyType', 'cardChargeAmount']
            strategy = await this.strategyRepository.findOne({
                where : {strategyId},
                select : fields
            })

            if(strategy) {
                this.localCache.store.set(key, JSON.stringify(strategy), {EX : 60*10})
            }else {
                return this.defaultStrategy()
            }
        }

        return strategy
    }

    async balance(cardNo:string) {
        const card = await this.card(cardNo)
        if(card) {
            this.localCache.store.set(this.cardKey + cardNo, JSON.stringify(card), {EX : 300})
        }
        return card
    }

    async charge(simCardNo:string, portNo:number, cardNo:string) {
        const device = await this.deviceService.device(simCardNo)
        const strategy = await this.strategy(device.strategyId)
        const amount = Number(strategy.cardChargeAmount)
        
        const time = Math.floor((new Date()).getTime() / 1000)

        if(!device) return {result : 0}
        
        const orderNo = ''
        let order:{[key in keyof ChargeCardOrder]? : ChargeCardOrder[key]} = {
            orderNo : orderNo,
            cardNo : cardNo,
            deviceId : device.deviceId,
            portNo : portNo,
            feeAmount : '0',
            feeRewardAmount : '0',
            chargeAmount : String(amount),
            strategyRules : strategy.rules,
            strategyType : strategy.strategyType,
            unitPrice : strategy.unitPrice,
            settleUnit : strategy.settleUnit,
            status : 1,
            createTime : time
        }

        order.prePayAmount = order.chargeAmount

        if(device.merchantId) {
            order.merchantId = device.merchantId
        }

        if(device.subMerchantId) {
            const subMerchant = await this.merchantRepository.findOne({merchantId : device.subMerchantId}, {select : ['profitRate']})
            if(subMerchant) {
                order.subMerchantId = device.subMerchantId
                order.profitRate = subMerchant.profitRate
            }
        }

        let isSuccess = false
        const queryRunner = await this.startTransaction()

        try {
            let card = await queryRunner.manager.findOne<ChargeCard>(ChargeCard, {
                where : {cardNo : cardNo},
                select : ['amount', 'rewardAmount', 'cardId', 'userId', 'status'],
                lock : {
                    mode : 'pessimistic_read'
                }
            })

            if(!card) return {result : 2}
            if(card.status != 1) return {result : 4}

            let rechargeBalance = Number(card.amount) //充值的余额
            let rewardBalance = Number(card.rewardAmount) //赠送的余额

            let balance = (rechargeBalance + rewardBalance) - amount
            if(balance < 0) return {result : 3}

            order.userId = card.userId
            order.cardId = card.cardId

            let { cardLog, cardUpdate} = this.cardUpdate(amount, rechargeBalance, rewardBalance, order, card)
            cardLog.type = 2

            {
                //更新卡
                const { affected } = await queryRunner.manager.update<ChargeCard>(ChargeCard, { cardId : card.cardId }, cardUpdate)
                if(affected < 1) {
                    return {result : 0}
                }
            }

            let orderId = 0

            {
                //加入订单
                const { raw } = await queryRunner.manager.insert<ChargeCardOrder>(ChargeCardOrder, order)
                if(!raw || raw.affectedRows < 1) {
                    return {result : 0}
                }
                cardLog.relationId = orderId = raw.insertId
            }

            {
                //加入卡明细
                cardLog.createTime = order.createTime
                const { raw } = await queryRunner.manager.insert<ChargeCardLog>(ChargeCardLog, cardLog)
                if(!raw || raw.affectedRows < 1) return {result : 0}
            }

            await queryRunner.commitTransaction()

            isSuccess = true

            return {
                orderMsgId : orderId,
                result : 1,
                remainMoney : balance
            }


        } catch (error) {
            console.log(error)
        }finally {
            if(!isSuccess) {
                await queryRunner.rollbackTransaction()
            }
            await queryRunner.release()
        }

        return {result : 0}
    }

    async addCharge(orderId:number) {
        const chargeOrder = await this.orderRepository.findOne({
            where : {orderId},
            select : ['chargeAmount', 'feeAmount', 'feeRewardAmount', 'cardId', 'userId', 'prePayAmount']
        })
        if(!chargeOrder) return {result : 2}
        if(chargeOrder.status != 1) return {result : 0}

        const amount = Number(chargeOrder.prePayAmount)

        let order:{[key in keyof ChargeCardOrder]? : ChargeCardOrder[key]} = {
            prePayAmount : chargeOrder.prePayAmount,
            userId : chargeOrder.userId,
            cardId : chargeOrder.cardId
        }

        let orderUpdate:{[key in keyof ChargeCardOrder]? : ChargeCardOrder[key]} = {
            chargeAmount : String(Number(chargeOrder.chargeAmount) + amount)
        }
        const time = Math.floor((new Date()).getTime() / 1000)
        let isSuccess = false
        const queryRunner = await this.startTransaction()

        try {

            let card = await queryRunner.manager.findOne<ChargeCard>(ChargeCard, {
                where : {cardId : chargeOrder.cardId},
                select : ['amount', 'rewardAmount'],
                lock : {
                    mode : 'pessimistic_read'
                }
            })

            let rechargeBalance = Number(card.amount) //充值的余额
            let rewardBalance = Number(card.rewardAmount) //赠送的余额
            let balance = (rechargeBalance + rewardBalance) - amount
            if(balance < 0) return {result : 3}

            let { cardLog, cardUpdate} = this.cardUpdate(amount, rechargeBalance, rewardBalance, order, card)
            cardLog.type = 2
            cardLog.relationId = orderId

            if(order.feeAmount) {
                orderUpdate.feeAmount = String(Number(chargeOrder.feeAmount) + Number(order.feeAmount))
            }
            if(order.feeRewardAmount) {
                orderUpdate.feeRewardAmount = String(Number(chargeOrder.feeRewardAmount) + Number(order.feeRewardAmount))
            }

            {
                //更新卡
                const { affected } = await queryRunner.manager.update<ChargeCard>(ChargeCard, { cardId : chargeOrder.cardId }, cardUpdate)
                if(affected < 1) {
                    return {result : 0}
                }
            }

            {
                //更新订单
                const { affected } = await queryRunner.manager.update<ChargeCardOrder>(ChargeCardOrder, {orderId : orderId}, orderUpdate)
                if(affected < 1) {
                    return {result : 0}
                }
            }

            {
                //加入卡明细
                cardLog.createTime = time
                const { raw } = await queryRunner.manager.insert<ChargeCardLog>(ChargeCardLog, cardLog)
                if(!raw || raw.affectedRows < 1) return {result : 0}
            }

            await queryRunner.commitTransaction()

            isSuccess = true

            return {
                orderMsgId : orderId,
                result : 1,
                remainMoney : balance
            }



            
        } catch (error) {
            console.log(error)
        } finally {
            if(!isSuccess) {
                await queryRunner.rollbackTransaction()
            }
            await queryRunner.release()
        }

        return {result : 0}

    }
}
